#include "LRUCache.h"

LRUCache::LRUCache()
{
    _capacity = DEFAULT_INIT_CAPACITY;
    _size = 0;
    _HeadOfList = new CacheNode;
    _EndOfList = new CacheNode;
    _HeadOfList->last = nullptr;
    _HeadOfList->next = _EndOfList;
    _EndOfList->last = _HeadOfList;
    _EndOfList->next = nullptr;
}

LRUCache::LRUCache(int capacity)
{
    _capacity = capacity;
    _size = 0;
    _HeadOfList = new CacheNode;
    _EndOfList = new CacheNode;
    _HeadOfList->last = nullptr;
    _HeadOfList->next = _EndOfList;
    _EndOfList->last = _HeadOfList;
    _EndOfList->next = nullptr;
}

LRUCache::~LRUCache()
{
    CacheNode* tempptr = _HeadOfList->next;
    while (tempptr != nullptr)
    {
        safe_delete(tempptr->last);
        tempptr = tempptr->next;
    }
    safe_delete(_EndOfList);
    _size = 0;
    _capacity = 0;
}

bool LRUCache::AddNode(LRUCache::CacheNode* node)
{
    node->next = _HeadOfList->next;
    (_HeadOfList->next)->last = node;
    _HeadOfList->next = node;
    node->last = _HeadOfList;
    ++_size;
    return true;
}

bool LRUCache::DetachNode(LRUCache::CacheNode* node)
{
    (node->last)->next = node->next;
    (node->next)->last = node->last;
    --_size;
    return true;
}

LRUCache::ValueType LRUCache::get(LRUCache::KeyType key)
{
    CacheNode* ptr = _HeadOfList->next;

    while (ptr->next != nullptr)
    {
        if (ptr->key == key)
        {
            DetachNode(ptr);
            AddNode(ptr);
            return ptr->value;
        }

        ptr = ptr->next;
    }

    return KEY_NOT_FOUND;
}

bool LRUCache::put(LRUCache::KeyType key, LRUCache::ValueType value)
{
    CacheNode* ptr = _HeadOfList->next;

    while (ptr->next != nullptr)
    {
        if (ptr->key == key)
        {
            ptr->value = value;
            get(key); // Refresh the node to be the most recently accessed one.
            return true;
        }

        ptr = ptr->next;

    }

    if (_size >= _capacity)
    {
        ptr = _EndOfList->last;
        DetachNode(ptr);
        safe_delete(ptr);
    }

    ptr = new CacheNode;

    if (ptr == nullptr)
    {
        exit(-1);
    }

    ptr->key = key;
    ptr->value = value;
    AddNode(ptr);
    
    return true;
}

int LRUCache::size(void)
{
    return _size;
}

int LRUCache::capacity(void)
{
    return _capacity;
}